%%%-------------------------------------------------------------------
%%% @author liuwentao
%%% @doc
%%%
%%% @end
%%% Created : 24. 5月 2021 14:19
%%%-------------------------------------------------------------------
-module(jmake).
-author("liuwentao").

-include("common.hrl").

%% API
-export([
    u/1
    , u/0
]).

%% @doc 热更新所有模块(非强制)
-spec u() -> ok.
u() ->
    do_up([], false).



%% 执行更新
do_up(L, F) ->
    ?INFO("--- 正在热更新节点: ~w ---~n", [node()]),
    Args = case {L, F} of
               {[], false}         -> [];
               {[], true}          -> [force];
               {[_H | _T], false}  -> [L];
               {[_H | _T], true}   -> [L, force]
           end,
    print_up(apply(make_code, up, Args)).

%% 显示更新结果
print_up([]) -> ?INFO("~n");
print_up([{M, ok} | T]) ->
    ?INFO("# 加载模块成功: ~p~n", [M]),
    print_up(T);
print_up([{M, {error, Reason}} | T]) ->
    ?INFO("* 加载模块失败[~p]: ~p~n", [M, Reason]),
    print_up(T).

%% @doc 单独热更摸个模块
u(Mod) ->
    %% 获取当前工作目录
    {ok, Path} = file:get_cwd(),
    catch make(Mod),
    %% 设置当前工作目录
    file:set_cwd(Path),
    %% 热更新
    make_code:up().


%% @doc 编译模块代码
make(Mod) ->
    CodePath = ?code_path,
    file:set_cwd(CodePath),
    {ok, [{CodeList, _Opts}]} = file:consult('Emakefile'),
    %% 找出所有的模块代码
    AllFiles = file_list(CodeList, []),
    File = case is_list(Mod) of
               true -> Mod;
               _ -> atom_to_list(Mod)
           end,
    %% 找到路径含有的模块
    MakeFiles = match_file(AllFiles, File, []),
    %% 编译一系列模块文件
    make:files(MakeFiles, [{i, ?code_path ++ "/inc"}, {outdir, ?code_path ++ "/ebin"}]),
    ok.


%% @doc 找出所有代码模块
file_list([], List) -> List;
file_list([Atom | T], L) when is_atom(Atom) ->
    List = filelib:wildcard(atom_to_list(Atom) ++ ".erl"),
    file_list(T, List ++ L);
file_list([Dir | T], L) when is_list(Dir) ->
    List = filelib:wildcard(Dir ++ ".erl"),
    file_list(T, List ++ L);
file_list([_ | T], L) ->
    file_list(T, L).

%% @doc 找出路径中包含文件名的模块
match_file([], _SubStr, FilePaths) -> FilePaths;
match_file([Dir | T], SubStr, L) ->
    case string:str(Dir, SubStr) of
        Int when Int > 0 -> match_file(T, SubStr, [Dir | L]);
        _ -> match_file(T, SubStr, L)
    end.



